package net.gdface.cassdk;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import net.gdface.utils.ConfigUtils;
import net.gdface.utils.Configuration;

/**
 * CASSDK配置参数管理<br>
 * 类初始化时要加载conf/cassdk.properties文件,加载顺序为:<br>
 * 1.调用{@link ClassLoader#getResource(String)}方法在class所在位置查找conf/cassdk.properties<br>
 * 2.如果class在jar包中,则尝试读取在jar所在位置../conf/cassdk.properties,tomcat下即为WEB-INF/conf/cassdk.properties<br>
 * 3.user.dir下查找conf/cassdk.properties加载配置<br>
 * 后面的配置变量会覆盖前面的定义<br>
 * {@link JniBridge}会在静态初始化时读取此类的配置用于初始化JNI接口<br>
 * 如果需要重新设置参数需要{@link JniBridge}加载前调用设置property的系列方法(如{@link #setProperty(String, String)}<br>
 * 设置参数才有效
 * @author guyadong
 *
 */
public class CasConfig extends Configuration {
	private static final long serialVersionUID = 5861262246470501912L;

	/** 当前CASSDK版本号 */
	private static Version version;
	private static Configuration instance=new Configuration();
	private static final String CONF_FOLDER="conf";
	private static final String HOME_FOLDER= ".cassdk";
	private static final String PROP_FILE="cassdk.properties";
	public static final String PROP_MIN_FACE_SIZE="THFI_Param.nMinFaceSize";
	public static final String PROP_ROLL_ANGLE="THFI_Param.nRollAngle";
	public static final String PROP_CHANNEL_NUM_DETECT="nChannelNum.detect";
	public static final String PROP_CHANNEL_NUM_FEATURE="nChannelNum.feature";
	public static final String PROP_MAX_DETECT_FACE_NUM="nMaxDetectFaceNum";
	public static final String PROP_SAMPLESIZE="nSampleSize";
	public static final String PROP_DEVICE_ID = "nDeviceID";
	public static final String PROP_GPU_MODE="gpuMode";	
	public static final String PROP_CASSDK_VERSION = "version";
	public static final String PROP_LICENSECODE = "licenseCode";
	/**
	 * 默认最小人脸尺寸
	 */
	public static final int DEFAULT_MIN_FACE_SIZE=50;
	/**
	 * 默认人脸角度 
	 */
	public static final int DEFAULT_ROLL_ANGLE=30;
	/**
	 * 默认通道数
	 */
	public static final int DEFAULT_CHANNEL_NUM=Runtime.getRuntime().availableProcessors();
	/**
	 * 默认最大检测人脸数目
	 */
	public static final int DEFAULT_MAX_DETECT_FACE_NUM=256;
	/**
	 * 默认图像归一化尺寸
	 */
	public static final int DEFAULT_SAMPLE_SIZE=640;
	/**
	 * 默认GPU设备ID
	 */
	public static final int DEFAULT_DEVICE_ID=0;
	/**
	 * 默认运行模式
	 */
	public static final boolean DEFAULT_GPU_MODE = false;
	static{
		instance.putAll(ConfigUtils.loadAllProperties(PROP_FILE, CONF_FOLDER, null, CasConfig.class, false));	
		instance.putAll(ConfigUtils.loadPropertiesInUserHome(HOME_FOLDER + "/" + PROP_FILE));
	}
	/**
	 * @return instance
	 */
	public static Configuration getInstance() {
		return instance;
	}
	/**
	 * 获取最小检测人脸尺寸<br>
	 * 如果参数表中没找到 {@value #PROP_MIN_FACE_SIZE},则返回 {@value #DEFAULT_MIN_FACE_SIZE}
	 * @param size
	 */
	public static int getMinFaceSize(){
		return instance.getPropertyInteger(PROP_MIN_FACE_SIZE, DEFAULT_MIN_FACE_SIZE);
	}
	/**
	 * 获取人脸角度<br>
	 * 如果参数表中没找到 {@value #PROP_ROLL_ANGLE},则返回 {@value #DEFAULT_ROLL_ANGLE}
	 */
	public static int getRollAngle(){
		return instance.getPropertyInteger(PROP_ROLL_ANGLE,DEFAULT_ROLL_ANGLE);
	}
	/**
	 * 如果 {@value #PROP_CHANNEL_NUM_DETECT}未定义则使用默认值 {@value #DEFAULT_CHANNEL_NUM}
	 * @return 返回人脸检测初始化通道数
	 */
	public static int getDetectChannelNum(){
		// GPU模式下通道数只能为1
		return isGpuMode() ? 1 : instance.getPropertyInteger(PROP_CHANNEL_NUM_DETECT,DEFAULT_CHANNEL_NUM);
	}
	/**
	 * 如果 {@value #PROP_CHANNEL_NUM_FEATURE}未定义则使用默认值 {@value #DEFAULT_CHANNEL_NUM}
	 * @return 返回特征提取初始化通道数
	 */
	public static int getFeatureChannelNum(){
		// GPU模式下通道数只能为1
		return isGpuMode() ? 1 : instance.getPropertyInteger(PROP_CHANNEL_NUM_FEATURE,DEFAULT_CHANNEL_NUM);
	}
	/**
	 * 设置特征提取和人脸检测通道数<br>
	 * SDK初始化前调用才生效<br>
	 * 调用{@link #saveConfig()}保存到配置文件才能在下次启动时生效
	 * @param channelNum
	 */
	public static void setChannelNum(int channelNum){
		instance.setPropertyInteger(PROP_CHANNEL_NUM_DETECT, channelNum);
		instance.setPropertyInteger(PROP_CHANNEL_NUM_FEATURE, channelNum);
	}
	/**
	 * 如果没有检测和特征提取通道数，则使用指定的值{@code channelNum}初始化
	 * @param channelNum
	 */
	public static void setChannelNumIfAbsent(int channelNum){
		instance.setPropertyIntegerIfAbsent(PROP_CHANNEL_NUM_DETECT, channelNum);
		instance.setPropertyIntegerIfAbsent(PROP_CHANNEL_NUM_FEATURE, channelNum);
	}
	/**
	 * 如果 {@value #PROP_MAX_DETECT_FACE_NUM}未定义则使用默认值 {@value #DEFAULT_MAX_DETECT_FACE_NUM}
	 * @return 返回最大可检测人脸数目
	 */
	public static int getMaxDetectFaceNum(){
		return instance.getPropertyInteger(PROP_MAX_DETECT_FACE_NUM,DEFAULT_MAX_DETECT_FACE_NUM);
	}
	/**
	 * 如果 {@value #PROP_SAMPLESIZE}未定义则使用默认值 {@value #DEFAULT_SAMPLE_SIZE}
	 * @return
	 */
	public static int getSampleSize(){
		return instance.getPropertyInteger(PROP_SAMPLESIZE,DEFAULT_SAMPLE_SIZE);
	}
	/**
	 * 如果 {@value #PROP_DEVICE_ID}未定义则使用默认值 {@value #DEFAULT_DEVICE_ID}
	 * @return
	 */
	public static int getDeviceID(){
		return instance.getPropertyInteger(PROP_DEVICE_ID,DEFAULT_DEVICE_ID);
	}
	/**
	 * 如果 {@value #PROP_GPU_MODE}未定义则使用默认值 {@value #DEFAULT_GPU_MODE}
	 * @return
	 */
	public static boolean isGpuMode(){
		return instance.getPropertyBoolean(PROP_GPU_MODE,DEFAULT_GPU_MODE);
	}
	/**
	 * 返回配置文件中保存的授权码，如果没有返回{@code null}
	 * @return
	 */
	public static String getLicenseCode(){
		return  instance.getProperty(PROP_LICENSECODE);
	}
	/**
	 * 设置授权码<br>
	 * SDK初始化前调用才生效<br>
	 * 调用{@link #saveConfig()}保存到配置文件才能在下次启动时生效
	 * @param licenseCode
	 */
	public static void setLicenseCode(String licenseCode){
		if(null != licenseCode && !licenseCode.isEmpty()){
			instance.setProperty(PROP_LICENSECODE, licenseCode);
		}
	}
	/**
	 * 保存license code到配置文件,只需要在SDK安装时调用一次，不需要每次程序启动都调用<br>
	 * 也可以将获取的license code手工写到 {@code ${user.home}/.cassdk/cassdk.properties}中,格式如下
	 * <pre>
	 * licenseCode=xxxxxxx
	 * </pre
	 * @param licenseCode
	 */
	public static void saveLicenseCode(String licenseCode){
		if(null !=licenseCode && !licenseCode.isEmpty()){
			setLicenseCode(licenseCode);
			saveConfig();
		}		
	}
	/**
	 * 保存当前配置到{@code ${user.home}/.cassdk/cassdk.properties}
	 */
	public static void saveConfig(){
		try {
			ConfigUtils.storePropertiesInUserHome(instance, HOME_FOLDER + "/" + PROP_FILE);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	static List<String> getExplodedStringAsList(String value) {
		ArrayList<String> al = new ArrayList<String>();
		if (value == null) {
			return al;
		}
		StringTokenizer st = new StringTokenizer(value, " ,;\t\n\r\f");
		while (st.hasMoreTokens()) {
			al.add(st.nextToken().trim());
		}
		return al;
	}
	/**
	 * 从配置文件(properties)读取版本号{@value #PROP_CASSDK_VERSION}
	 * @return
	 */
	public synchronized static Version getVersion(){
		if(null == version){
			String value = instance.getProperty(PROP_CASSDK_VERSION);	
			version = Version.cast(value);
		}
		return version;
	}
	/**
	 * 设置CASSDK初始化参数<br>
	 * 参数名称(key)参见{@link CasConfig}中所有{@code PROP_}前缀的常量
	 * @param properties 为{@code null}时忽略
	 */
	public synchronized static void setConfig(Map<String,String> properties){
		if(null != properties){
			instance.putAll(properties);
			instance.list(System.out);
		}		
	}
}
